/**
 * ChatClient
 *
 *
 *
 *
 *
 *
 *
 **/
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <string>
#include <vector>
#include <map>
#include <boost/regex.hpp>

#ifndef CHATCLIENT_H
#define CHATCLIENT_H
class ChatClient
{
    public:
        static const int RECV_BUF_ARRAY_SIZE; // Max buffer size
        static const int SEND_BUF_ARRAY_SIZE; // Max buffer size
        static const int CLIENT_ONLINE;
        static const int CLIENT_OFFLINE;
        static const int CLIENT_MSGS;
        static const std::string PROMPT;
        
        ChatClient();
        ChatClient(int port);
        ChatClient( const char * p_nickname, 
                    const char * p_dotted_ip, 
                    int server_port, 
                    int client_port );
        ~ChatClient();
        
        // Mode Agnostic Functions
        int  start(int attempts);   // Binds and starts the program's socket
        void stop();                // Stops and closes the program's socket
        int  listen();              // Makes the program listen for data coming into the socket
        void parse_data();          // CLIENT and SERVER Based Processing -- calls appropriate parser based on current mode
        void print_socket_info();   // By default prints this Program's IP and Port socket info
        void print_socket_info(sockaddr_in & p_sock); // Prints Info about a particular socket
        bool isRunning();
        bool validate_recv_packet_header(); // Validates incoming socket packet header
        void construct_packet_header(char packet_type);
        void update_client_table(std::string & nickname, int client_status, sockaddr_in & client_ip_port); // CLIENT and SERVER -- Inserts / Updates single entry in client table
        void print_client_table();
        int  demultiplex(int wait_in_milliseconds);              // We should be able to listen to both the input port and STDIN at the same time
        void process_stdin();       // Process STDIN, read STDIN and process
        int  handle_send_dereg_request();
        int  handle_send_message(std::string & nickname, std::string & msg, bool need_timestamp = false);
        bool listen_for_ack(int wait_in_milliseconds, sockaddr_in & src);
        void send_ack(sockaddr_in & dst);
        void send_confirmation(char c, sockaddr_in & dst);

        // SERVER Functions
        void register_client(std::string nickname, sockaddr_in & addrport);     // SERVER uses this to update the client table
        void deregister_client(std::string nickname, sockaddr_in & addrport);   // SERVER uses this to update the client table.
        int  populate_client_table_packet_data();                               // SERVER uses this to generate the client table packet
        void broadcast_client_status();                                         // SERVER uses this to send its client table to all the online clients
        void parse_data_server();   // SERVER's data processing stream

        // CLIENT Functions
        void handle_register_client();
        void register_client();     // CLIENT Uses this to register with the server
        void deregister_client();   // CLIENT Uses this to de-register with the server
        void parse_client_table_packet();   // CLIENT Parses the incoming packet -- this is recieved from the server
        void parse_data_client();   // CLIENT's data processing stream
        int  listen_for_reg_response(int wait_in_milliseconds, sockaddr_in & src);
    private:

        std::string m_nickname; // The client's current nickname
        int m_client_port;      // The client's current send/recieve port
        int m_server_port;      // The port to send info to server
        int m_program_socket_id;// Socket ID for the main socket that we're using for this program.
        bool m_is_registered;

        sockaddr_in m_server_ip_port; // IP and Port of the server, stored in network-byte order
        sockaddr_in m_client_ip_port; // IP and Port of the client, stored in network-byte order
        sockaddr_in m_remote_ip_port; // IP and Port of the current remote host we recieve from

        char * m_socket_recv_buffer;  // Buffer that we recv data into
        char * m_socket_send_buffer;  // Buffer that we send data from

        int m_socket_recv_buffer_size; // Size of the above buffer
        int m_socket_send_buffer_size; // Size of the above buffer

        bool m_is_running; // Is the socket currently active and in use
        bool m_is_server;  // TRUE -- client is running in server mode. FALSE -- client is running as client

        int m_recv_errorno; // The most recent recvfrom errorno
        int m_send_errorno; // The most recent sendto errorno
        
        // Chat application specific parameters
        // Client Table parameters
        
        typedef std::map<std::string, std::pair<int, sockaddr_in> > client_table_type;
        typedef std::pair<std::string, std::string> offline_message_type;
        // offline message type: (sender, TIMESTAMPED message)

        std::map<std::string, std::pair<int, sockaddr_in> > c_client_table;
        std::map<std::string, std::list<offline_message_type> > offline_message_table;

        // Key = Client Nickname
        // Value = Pair <Online Status, Socket Information>
};

#endif /* CHATCLIENT_H */
